## Vue 实现双向数据绑定原理是什么

<details>
<summary>答案</summary>

Vue2.x 采用数据劫持结合发布订阅模式（PubSub 模式）的方式，通过 Object.defineProperty 来劫持各个属性的 setter、getter，在数据变动时发布消息给订阅者，触发相应的监听回调。

当把一个普通 Javascript 对象传给 Vue 实例来作为它的 data 选项时，Vue 将遍历它的属性，用 Object.defineProperty 将它们转为 getter/setter。用户看不到 getter/setter，但是在内部它们让 Vue 追踪依赖，在属性被访问和修改时通知变化。

Vue 的数据双向绑定整合了 Observer，Compile 和 Watcher 三者，通过 Observer 来监听自己的 model 的数据变化，通过 Compile 来解析编译模板指令，最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁，达到数据变化->视图更新，视图交互变化（例如 input 操作）->数据 model 变更的双向绑定效果。

Vue3.x 放弃了 Object.defineProperty ，使用 ES6 原生的 Proxy，来解决以前使用 Object.defineProperty 所存在的一些问题。

</details>

## Vue 的 nextTick 是如何实现的

<details>
<summary>答案</summary>
nextTick 的本质将回调函数包装为一个微任务放入到微任务队列，这样浏览器在完成渲染任务后会优先执行微任务。
nextTick 在 Vue2 和 Vue3 里的实现有一些不同：
1. Vue2 为了兼容旧浏览器，会根据不同的环境选择不同包装策略：
  - 优先使用 Promise，因为它是现代浏览器中最有效的微任务实现。
  - 如果不支持 Promise，则使用 MutationObserver，这是另一种微任务机制。
  - 在 IE 环境下，使用 setImmediate，这是一种表现接近微任务的宏任务。
  - 最后是 setTimeout(fn, 0) 作为兜底方案，这是一个宏任务，但会在下一个事件循环中尽快执行。
2. Vue3 则是只考虑现代浏览器环境，直接使用 Promise 来实现微任务的包装，这样做的好处在于代码更加简洁，性能更高，因为不需要处理多种环境的兼容性问题。
整体来讲，Vue3 的 nextTick 实现更加简洁和高效，是基于现代浏览器环境的优化版本，而 Vue2 则为了兼容性考虑，实现层面存在更多的兼容性代码。
</details>
## Vue 的数据为什么频繁变化但只会更新一次
<details>
<summary>答案</summary>

这是因为 vue 的 DOM 更新是一个异步操作，在数据更新后会首先被 set 钩子监听到，但是不会马上执行 DOM 更新，而是在下一轮循环中执行更新。

具体实现是 vue 中实现了一个 queue 队列用于存放本次事件循环中的所有 watcher 更新，并且同一个 watcher 的更新只会被推入队列一次，并在本轮事件循环的微任务执行结束后执行此更新(UI Render 阶段)，这就是 DOM 只会更新一次的原因。

这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后，在下一个的事件循环“tick”中，vue 刷新队列并执行实际 (已去重的) 工作。vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate，如果执行环境不支持，则会采用 setTimeout(fn, 0) 代替。

</details>
